import {
  isArray,
  isObject,
  isFunction,
  isEmpty,
  isInteger,
  toNumber,
  notArray,
  notEmpty,
  notPlainObject,
  toBoolean,
  toDate,
  notString,
} from "./utils.js";

type Values = Record<string, any>;

// 通用验证函数类型
type Validator = (value: any, param?: any, values?: Values) => Promise<void>;

// 可转换数据类型
type ConvertType = "String" | "Number" | "Boolean" | "Date";

// 验证规则描述项
interface RuleItem {
  type?: ConvertType;
  required?: boolean;
  message?: string;
  validate?: Validator;
  [propName: string]: any;
}

type Rule = RuleItem | Array<RuleItem>;

type Rules = Record<string, Rule>;

// 验证器验证错误返回类型
type ValidatorErrorResult = Array<string>;
// 验证函数错误返回类型
type ValidateErrorResult = Record<string, ValidatorErrorResult>;

/**
 * 验证函数类型
 */
type ValidateFunc = (val: any, values?: Values) => Promise<void>;

/**
 * 验证函数数组类型
 */
interface ValidateArray extends Array<ValidateFunc> {
  type?: Function;
  required?: boolean;
  message?: string;
}
/**
 * ValidatorError 验证器执行错误
 *
 * messages: Array<string> : 验证错误数组
 */
class ValidatorError extends Error {
  public messages: ValidatorErrorResult;

  constructor(messages: ValidatorErrorResult) {
    super(messages.join(", "));
    this.messages = messages;
  }
}

/**
 * ValidateError 验证方法执行错误
 *
 * errors: Record<string, Array<string>> : { fieldName: ['error message', ...], ...}
 */
class ValidateError extends Error {
  public errors: ValidateErrorResult;
  constructor(errors: ValidateErrorResult) {
    super("validate error");
    this.errors = errors;
  }
}

const mustNumber = (val: any) => {
    if (Number.isNaN(+val)) throw new Error("必须输入数字");
  },
  regEmail = /^[A-Za-z0-9\u4e00-\u9fa5]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/,
  /**
   * 类型转换函数
   */
  converter: Record<ConvertType, Function> = {
    String: (val: any) => val.trim(),
    Number: (val: any) => toNumber(val),
    Boolean: (val: any) => toBoolean(val),
    Date: (val: any) => toDate(val),
  },
  /**
   * 空值判定
   */
  valueIsEmpty = (value: any) => {
    if (notString(value) && notPlainObject(value) && notArray(value))
      return false;
    return isEmpty(value);
  },
  /**
   * 内置验证器
   */
  validators: Record<string, Validator> = {
    async minLength(value, param) {
      if (value.length < param) throw new Error("最少" + param + "个字符");
    },
    async maxLength(value, param) {
      if (value.length > param) throw new Error("不能超过" + param + "个");
    },
    async len(value, param) {
      if (value.length != param)
        throw new Error("限制长度必须是" + param + "个字符");
    },
    async isInteger(value) {
      if (!isInteger(value)) throw new Error("必须是整数");
    },
    async isIn(value, param) {
      if (notArray(param)) throw new Error("isIn的验证参数必须是数组");
      if (!param.includes(value)) throw new Error(`必须是[${param}]`);
    },
    async min(value, param) {
      mustNumber(value);
      if (+value < param) throw new Error("必须大于等于" + param);
    },
    async max(value, param) {
      mustNumber(value);
      if (+value > param) throw new Error("必须小于等于" + param);
    },
    async same(value, param, values) {
      if (values == undefined || param == undefined)
        throw new Error("same规则的参数错误");
      if (value !== values[param]) throw new Error(`和[${param}]不匹配！`);
    },
    async email(value) {
      if (!regEmail.test(value)) throw new Error("不是合法的Email");
    },
    async regex(value, param) {
      if (param.test(value)) throw new Error("不符合格式要求");
    },
  },
  /**
   * addValidator 注册新验证器
   *
   * @param {string} name 验证器名称
   * @param {function} fn 验证器函数
   * @return {void}
   */
  addValidator = (name: string, fn: Validator) => {
    if (validators[name] || ["message", "validate"].includes(name))
      throw new Error(`${name} was error or already exists`);
    validators[name] = fn;
  },
  /**
   * parseRule 解析验证规则
   *
   * @param {Rule} rule: 验证规则描述
   *
   *        { type: Number, required: true, minLength: 6, ..., async validate(value) {}, message: '自定义错误消息' }
   *
   *        或者 [{ required: true, message: '自定义错误消息' }, {...}, ...]
   * @returns {ValidateArray}
   */
  parseRule = (rule: Rule) => {
    const validateArray: ValidateArray = [],
      _parseRule = (rule: RuleItem) => {
        const {
          type,
          required = false,
          message,
          validate,
          ...descriptions
        } = rule;
        // type：数据类型转换函数
        validateArray.type = type != undefined ? converter[type] : type;
        // required: 是否允许为空
        if (!validateArray.required) {
          validateArray.required = required;
          validateArray.message = message;
        }
        // validate: 自定义验证函数
        if (isFunction(validate)) validateArray.push(validate as ValidateFunc);
        // 生成包含验证方法的数组
        Object.entries(descriptions).forEach(([k, v]) => {
          if (k in validators)
            validateArray.push(async (val: any, values?: Values) => {
              try {
                await validators[k](val, v, values);
              } catch (err: any) {
                throw new Error(message || err.message);
              }
            });
        });
      };
    // rule: Rule
    if (isArray(rule)) {
      for (const i of rule) _parseRule(i);
    } else _parseRule(rule);
    return validateArray;
  },
  /**
   * makeValidate 生成验证函数
   *
   * @param rules: 验证规则
   * @return Function
   */
  makeValidators = (rule: Rule | undefined) => {
    if (rule == undefined) return async (value: any) => value;
    // 验证函数数组
    const validatorArray = parseRule(rule);
    // 对单个值进行验证的函数
    return async (value: any, values?: Values) => {
      // 进行空值判定
      if (validatorArray.required && valueIsEmpty(value))
        throw new ValidatorError([validatorArray.message || "不能为空值"]);
      // 空值则不需继续执行验证方法
      if (valueIsEmpty(value)) return value;
      // 避免由于value是 undefined 或者 null 导致后续验证函数错误
      if (validatorArray.type) value = validatorArray.type(value);
      // 执行验证函数: 收集验证错误消息
      const messages: ValidatorErrorResult = [];
      for (const validator of validatorArray) {
        try {
          await validator(value, values);
        } catch (err: any) {
          messages.push(err.message);
        }
      }
      if (notEmpty(messages)) throw new ValidatorError(messages);
      // 返回
      return value;
    };
  },
  /**
   * schema 根据验证规则对多值进行验证
   *
   * @param rules:
   * @returns
   */
  schema = (rules: Rules = {}) => {
    const validateSchema: Record<string, ValidateFunc> = {};
    for (const i in rules) validateSchema[i] = makeValidators(rules[i]);
    // 返回验证函数
    return async (values: Values) => {
      const result: Values = {},
        errors: ValidateErrorResult = {};
      for (const it in values) {
        // 如果存在验证方法，则进行验证。
        if (it in validateSchema) {
          try {
            result[it] = await validateSchema[it](values[it], values);
          } catch (err: any) {
            errors[it] = err.messages;
          }
        } else result[it] = values[it];
      }
      if (notEmpty(errors)) throw new ValidateError(errors);
      return result;
    };
  },
  /**
   * validate
   *
   * @param values: 需要被验证的值
   * @param rules: 验证规则描述：
   *        {
   *          required: true, minLength: 6, ...,
   *          async validate(value) {},
   *          message: '自定义错误消息'
   *        }
   *        或者 [{ required: true, message: '自定义错误消息' }, {...}, ...]
   * @return Promise
   */
  validate = async (values: Values, rules: Rules) => {
    const v = schema(rules);
    return await v(values);
  };
export {
  Values,
  Validator,
  ValidateFunc,
  ValidateError,
  ValidateErrorResult,
  ValidatorError,
  ValidatorErrorResult,
  RuleItem,
  Rule,
  Rules,
  addValidator,
  makeValidators,
  schema,
  validate,
};
export default validate;
